=begin pod :kind("Type") :subkind("class") :category("domain-specific")

=TITLE class IO::Path

=SUBTITLE File or directory path

=for code
class IO::Path is Cool does IO { }

C<IO::Path> is the workhorse of IO operations.

Conceptually, an C<IO::Path> object consists of a volume, a directory,
and a basename. It supports both purely textual operations, and
operations that access the filesystem, e.g. to resolve a path, or to
read all the content of a file.

At creation, each C<IO::Path> object is given information about the
current working directory the path might be relative to using the
C<$.CWD> attribute (defaults to
L«C<$*CWD>|/language/variables#Dynamic_variables»), as well as what
operating system semantics should be used for path manipulation using
the special L«C<IO::Spec>|/type/IO::Spec» type given in the C<$.SPEC>
attribute.

The C<$.SPEC> defaults to the value of
L«C<$*SPEC>|/language/variables#Dynamic_variables», which uses the object
suitable for the operating system the code is currently running on. This is
the default most code will be comfortable with.

In certain situations, e.g. testing, you may wish to force C<$*SPEC> to use one
of the specific SPEC modules: L«C<IO::Spec::Unix>|/type/IO::Spec::Unix»,
L«C<IO::Spec::Win32>|/type/IO::Spec::Win32»,
L«C<IO::Spec::Cygwin>|/type/IO::Spec::Cygwin», and
L«C<IO::Spec::QNX>|/type/IO::Spec::QNX», or to create C<IO::Path> objects via
shortcut subclasses L«C<IO::Path::Unix>|/type/IO::Path::Unix»,
L«C<IO::Path::Win32>|/type/IO::Path::Win32»,
L«C<IO::Path::Cygwin>|/type/IO::Path::Cygwin», and
L«C<IO::Path::QNX>|/type/IO::Path::QNX» that pre-set the C<$.SPEC> attribute
for you.

The rest of this document silently assumes Unix semantics in its
examples, unless stated otherwise.

=head1 Methods

=head2 method new

Defined as:

=for code :skip-test<compile time error>
multi method new(Str:D $path, IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD)
multi method new(
    :$basename!, :$dirname = '.', :$volume = ''
    IO::Spec :$SPEC = $*SPEC, Str() :$CWD = $*CWD
)

Creates a new C<IO::Path> object from a path string (which is being
parsed for volume, directory name and basename), or from volume,
directory name and basename passed as named arguments.

The path's operation will be performed using C<:$SPEC> semantics (defaults to
current L«C<$*SPEC>|/language/variables#Dynamic_variables») and will use
C<:$CWD> as the directory the path is relative to (defaults to
L«C<$*CWD>|/language/variables#Dynamic_variables»).

If C<$path> includes the null byte, it will throw an Exception with a "Cannot
use null character (U+0000) as part of the path" message.


=head2 attribute CWD

    IO::Path.new("foo", :CWD</home/camelia>)
        .CWD.say; # OUTPUT: «/home/camelia␤»

Read-only. Contains implicit or explicit value of C<:$CWD> argument to C<.new>.

=head2 attribute SPEC

    IO::Path.new("foo", :SPEC(IO::Spec::Unix.new))\
        .SPEC.^name.say; # OUTPUT: «IO::Spec::Unix␤»

Read-only. Contains implicit or explicit value of C<:$SPEC> argument to C<.new>.

=head2 attribute path

    IO::Path.new("foo").path.say; # OUTPUT: «foo␤»

Read-only. Returns the string the object was constructed from or the value of
C<$SPEC.join($volume, $dirname, $basename)> if multi-part version of C<.new>
was used. B<NOTE:> this does not include the C<$.CWD>; see
L«C<IO::Path.absolute>|/routine/absolute»
and L«C<IO::Path.relative>|/routine/relative» for
stringification options that include C<$.CWD>.

B<NOTE:> Implementations may cache operations done with this attribute, so
modifying its value (via cloning or Proxy) is NOT recommended and may result
in broken C<IO::Path> objects. Create a new C<IO::Path> object instead.

=head2 method ACCEPTS

Defined as:

    multi method ACCEPTS(IO::Path:D: Cool:D $other --> Bool:D)

Coerces the argument to C<IO::Path>, if necessary. Returns C<True> if
L«C<.absolute>|/routine/absolute» method on both paths returns the same string.
B<NOTE:> it's possible for two paths that superficially point to the same
resource to NOT smartmatch as C<True>, if they were constructed differently and
were never fully resolved:

    say "foo/../bar".IO ~~ "bar".IO # False

The reason is the two paths above may point to different resources when fully
resolved (e.g. if C<foo> is a symlink). Resolve the paths before smartmatching
to check they point to same resource:

    say "foo/../bar".IO.resolve(:completely) ~~ "bar".IO.resolve(:completely) # True

=head2 method basename

Defined as:

    method basename(IO::Path:D:)

Returns the basename part of the path object, which is the name of the
filesystem object itself that is referenced by the path.

    "docs/README.pod".IO.basename.say; # OUTPUT: «README.pod␤»
    "/tmp/".IO.basename.say;           # OUTPUT: «tmp␤»

Note that in L«C<IO::Spec::Win32>|/type/IO::Spec::Win32» semantics, the
C<basename> of a Windows share is C<\>, not the name of the share itself:

    IO::Path::Win32.new('//server/share').basename.say; # OUTPUT: «\␤»

=head2 method add

Defined as:

    method add(IO::Path:D: Str() $what --> IO::Path:D)

Concatenates a path fragment to the invocant and returns the resultant
C<IO::Path>. If adding C<../> to paths that end with a file, you may
need to call L<resolve|/routine/resolve> for the resultant path to be accessible by other
C<IO::Path> methods like L<dir|/routine/dir> or L<open|/routine/open>. See also L<sibling|/routine/sibling> and
L<parent|/routine/parent>.

    "foo/bar".IO.mkdir;
    "foo/bar".IO.add("meow")    .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.add("/meow")   .resolve.relative.say; # OUTPUT: «foo/bar/meow␤»
    "foo/bar".IO.add("meow.txt").resolve.relative.say; # OUTPUT: «foo/bar/meow.txt␤»
    "foo/bar".IO.add("../meow") .resolve.relative.say; # OUTPUT: «foo/meow␤»
    "foo/bar".IO.add("../../")  .resolve.relative.say; # OUTPUT: «.␤»

=head2 method child

Defined as:

    method child(IO::Path:D: Str() $childname --> IO::Path:D)

Alias for L«C<.add>|/routine/add».

=head2 method cleanup

Defined as:

    method cleanup(IO::Path:D: --> IO::Path:D)

Returns a new path that is a canonical representation of the invocant path,
cleaning up any extraneous path parts:

    "foo/./././..////bar".IO.cleanup.say;      # OUTPUT: «"foo/../bar".IO␤»
    IO::Path::Win32.new("foo/./././..////bar")
        .cleanup.say; "foo\..\bar".IO;         # OUTPUT: «"foo\..\bar".IO␤»

Note that no filesystem access is made. See also
L«C<resolve>|/routine/resolve».

=head2 method comb

Defined as:

    method comb(IO::Path:D: |args --> Seq:D)

Opens the file and processes its contents the same way
L«C<Str.comb>|/type/Str#routine_comb» does, taking the same arguments.
Implementations may slurp the file in its entirety when this method is called.

=head2 method split

Defined as:

    method split(IO::Path:D: |args --> Seq:D)

Opens the file and processes its contents the same way
L«C<Str.split>|/type/Str#routine_split» does, taking the same arguments.
Implementations may slurp the file in its entirety when this method is called.

=head2 method extension

Defined as:

    multi method extension(IO::Path:D:                                         --> Str:D)
    multi method extension(IO::Path:D:               Int :$parts               --> Str:D)
    multi method extension(IO::Path:D:             Range :$parts               --> Str:D)
    multi method extension(IO::Path:D: Str $subst,   Int :$parts, Str :$joiner --> IO::Path:D)
    multi method extension(IO::Path:D: Str $subst, Range :$parts, Str :$joiner --> IO::Path:D)

Returns the extension consisting of C<$parts> parts (defaults to C<1>),
where a "part" is defined
as a dot followed by possibly-empty string up to the end of the string, or
previous part. That is C<"foo.tar.gz"> has an extension of two parts: first part
is C<"gz"> and second part is C<"tar"> and calling
C<"foo.tar.gz".IO.extension: :2parts> gives C<"tar.gz">. If an extension with
the specified number of C<$parts> is not found, returns an empty string.

C<$parts> can be a L«C<Range>|/type/Range», specifying the minimum number of
parts and maximum number of parts the extension should have. The routine will
attempt to much the most parts it can. If C<$parts> range's endpoints that are
smaller than C<0> they'll be treated as C<0>; implementations may treat
endpoints larger than C<2⁶³-1> as C<2⁶³-1>. Ranges with C<NaN> or
L«C<Str>|/type/Str» endpoints will cause an exception to be thrown.

If C<$subst> is provided, the extension will be instead replaced with C<$subst>
and a new C<IO::Path> object will be returned. It will be joined to the file's
name with C<$joiner>, which defaults to an empty string when C<$subst> is
an empty string and to C<"."> when C<$subst> is not empty. B<Note:> if as
the result of replacement the L«C<basename>|/routine/basename» of the path
ends up being empty, it will be assumed to be C<.> (a single dot).

    # Getting an extension:
    say "foo.tar.gz".IO.extension;               # OUTPUT: «gz␤»
    say "foo.tar.gz".IO.extension: :2parts;      # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(^5);   # OUTPUT: «tar.gz␤»
    say "foo.tar.gz".IO.extension: :parts(0..1); # OUTPUT: «gz␤»

    # Replacing an extension
    say "foo.tar.gz".IO.extension: '';                # OUTPUT: «"foo.tar".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP';             # OUTPUT: «"foo.tar.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts;    # OUTPUT: «"foo.tar.gz.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :2parts;    # OUTPUT: «"foo.ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :parts(^5); # OUTPUT: «"foo.ZIP".IO␤»

    # Replacing an extension using non-standard joiner:
    say "foo.tar.gz".IO.extension: '',    :joiner<_>;  # OUTPUT: «"foo.tar_".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>;  # OUTPUT: «"foo.tar_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :2parts;     # OUTPUT: «"foo_ZIP".IO␤»
    say "foo.tar.gz".IO.extension: 'ZIP', :joiner<_>,
                                           :parts(^5);  # OUTPUT: «"foo_ZIP".IO␤»

    # EDGE CASES:

    # There is no 5-part extension, so returned value is an empty string
    say "foo.tar.gz".IO.extension: :5parts; # OUTPUT: «␤»

    # There is no 5-part extension, so we replaced nothing:
    say "foo.tar.gz".IO.extension: 'ZIP', :5parts; # OUTPUT: «"foo.tar.gz".IO␤»

    # Replacing a 0-part extension is just appending:
    say "foo.tar.gz".IO.extension: 'ZIP', :0parts; # OUTPUT: «"foo.tar.gz.ZIP".IO␤»

    # Replace 1-part of the extension, using '.' joiner
    say "...".IO.extension: 'tar'; # OUTPUT: «"...tar".IO␤»

    # Replace 1-part of the extension, using empty string joiner
    say "...".IO.extension: 'tar', :joiner(''); # OUTPUT: «"..tar".IO␤»

    # Remove 1-part extension; results in empty basename, so result is ".".IO
    say ".".IO.extension: ''; # OUTPUT: «".".IO␤»

=head2 method dirname

Defined as:

    method dirname(IO::Path:D:)

Returns the directory name portion of the path object. That is, it returns
the path excluding the L<volume|/routine/volume> and the
L<base name|/routine/basename>. Unless the dirname consist of I<only> the directory
separator (i.e. it's the top directory), the trailing directory separator will I<not>
be included in the return value.

    say IO::Path.new("/home/camelia/myfile.p6").dirname; # OUTPUT: «/home/camelia␤»
    say IO::Path::Win32.new("C:/home/camelia").dirname;  # OUTPUT: «/home␤»
    say IO::Path.new("/home").dirname;                   # OUTPUT: «/␤»

=head2 method volume

Defined as:

    method volume(IO::Path:D:)

Returns the volume portion of the path object. On Unix system, this is always
the empty string.

    say IO::Path::Win32.new("C:\\Windows\\registry.ini").volume;    # OUTPUT: «C:␤»

=head2 method parts

Defined as:

    method parts(IO::Path:D:)

Returns a L«C<IO::Path::Parts>|/type/IO::Path::Parts» for the invocant.

    say IO::Path::Win32.new("C:/rakudo/raku.bat").parts.raku;
    # OUTPUT: «IO::Path::Parts.new("C:","/rakudo","raku.bat")␤»

B<Note>: Before Rakudo version 2020.06 a L«C<Map>|/type/Map» was
returned, with the keys C<volume>, C<dirname>, C<basename> whose values
were the respective invocant parts.

=head2 method raku

Defined as:

    method raku(IO::Path:D: --> Str:D)

Returns a string that, when given passed through L«C<EVAL>|/routine/EVAL»
gives the original invocant back.

=for code
"foo/bar".IO.raku.say;
# OUTPUT: IO::Path.new("foo/bar", :SPEC(IO::Spec::Unix), :CWD("/home/camelia"))

Note that this string includes the value of the C<.CWD> attribute that is set
to L«C<$*CWD>|/language/variables#Dynamic_variables» when the path
object was created, by default.

=head2 method gist

Defined as:

    method gist(IO::Path:D: --> Str:D)

Returns a string, part of which contains either the value of
L«C<.absolute>|/type/IO::Path#method_absolute» (if path is absolute) or
L«C<.path>|/type/IO::Path#attribute_path». Note that no escaping
of special characters is made, so e.g. C<"\b"> means a path contains a backslash
and letter "b", not a backspace.

    say "foo/bar".IO;                       # OUTPUT: «"foo/bar".IO␤»
    say IO::Path::Win32.new: ｢C:\foo/bar\｣; # OUTPUT: «"C:\foo/bar\".IO␤»

=head2 method Str

Defined as:

    method Str(IO::Path:D: --> Str)

Alias for L«C<IO::Path.path>|/routine/path». In particular, note that default
stringification of an C<IO::Path> does B<NOT> use the value of
L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». To stringify while
retaining full path information use L«C<.absolute>|/routine/absolute» or
L«C<.relative>|/routine/relative» methods.

=head2 method succ

Defined as:

    method succ(IO::Path:D: --> IO::Path:D)

Returns a new L<IO::Path|/type/IO::Path> constructed from the invocant, with
L«C<.basename>|/routine/basename» changed by calling
L«C<Str.succ>|/type/Str#method_succ» on it.

    "foo/file02.txt".IO.succ.say; # OUTPUT: «"foo/file03.txt".IO␤»

=head2 method open

Defined as:

    method open(IO::Path:D: *%opts)

Opens the path as a file; the named options control the mode, and are the
same as the L<open|/routine/open> function accepts.

=head2 method pred

Defined as:

    method pred(IO::Path:D: --> IO::Path:D)

Returns a new L<IO::Path|/type/IO::Path> constructed from the invocant, with
L«C<.basename>|/routine/basename» changed by calling
L«C<Str.pred>|/type/Str#method_pred» on it.

    "foo/file02.txt".IO.pred.say; # OUTPUT: «"foo/file01.txt".IO␤»

=head2 method watch

Defined as:

    method watch(IO::Path:D: --> Supply:D)

Equivalent to calling
L«IO::Notification.watch-path|/type/IO::Notification#method_watch-path»
with the invocant as the argument.

=head2 method is-absolute

Defined as:

    method is-absolute(IO::Path:D: --> Bool)

Returns C<True> if the path is an absolute path, and C<False> otherwise.

    "/foo".IO.is-absolute.say; # OUTPUT: «True␤»
    "bars".IO.is-absolute.say; # OUTPUT: «False␤»

Note that on Windows a path that starts with a slash or backslash is still
considered absolute even if no volume was given, as it is absolute for that
particular volume:

    IO::Path::Win32.new("/foo"  ).is-absolute.say; # OUTPUT: «True␤»
    IO::Path::Win32.new("C:/foo").is-absolute.say; # OUTPUT: «True␤»
    IO::Path::Win32.new("C:foo" ).is-absolute.say; # OUTPUT: «False␤»

=head2 method is-relative

Defined as:

    method is-relative(IO::Path:D: --> Bool)

Returns C<True> if the path is a relative path, and C<False> otherwise.
Windows caveats for L«C<.is-absolute>|/type/IO::Path#method_is-absolute»
apply.

=head2 method absolute

Defined as:

    multi method absolute(IO::Path:D: --> Str)
    multi method absolute(IO::Path:D: $base --> Str)

Returns a new C<Str> object that is an absolute path. If the invocant
is not already an absolute path, it is first made absolute using C<$base>
as base, if it is provided, or the C<.CWD> attribute the object was
created with if it is not.

=head2 method relative

Defined as:

    method relative(IO::Path:D: $base = $*CWD --> Str)

Returns a new C<Str> object with the path relative to the C<$base>. If C<$base>
is not provided, C<$*CWD> is used in its place. If the invocant is not
an absolute path, it's first made to be absolute using the C<.CWD>
attribute the object was created with, and then is made relative to C<$base>.

=head2 method parent

Defined as:

    multi method parent(IO::Path:D:)
    multi method parent(IO::Path:D: UInt:D $level)

Returns the parent path of the invocant. Note that no actual filesystem access
is made, so the returned parent is physical and not the logical parent of
symlinked directories.

    '/etc/foo'.IO.parent.say; # OUTPUT: «"/etc".IO␤»
    '/etc/..' .IO.parent.say; # OUTPUT: «"/etc".IO␤»
    '/etc/../'.IO.parent.say; # OUTPUT: «"/etc".IO␤»
    './'      .IO.parent.say; # OUTPUT: «"..".IO␤»
    'foo'     .IO.parent.say; # OUTPUT: «".".IO␤»
    '/'       .IO.parent.say; # OUTPUT: «"/".IO␤»
    IO::Path::Win32.new('C:/').parent.say; # OUTPUT: «"C:/".IO␤»

If C<$level> is specified, the call is equivalent to calling C<.parent()>
C<$level> times:

=for code
say "/etc/foo".IO.parent(2) eqv "/etc/foo".IO.parent.parent; # OUTPUT: «True␤»

=head2 method resolve

Defined as:

    method resolve(IO::Path:D: :$completely --> IO::Path)

Returns a new C<IO::Path> object with all symbolic links and references to the
parent directory (C<..>) resolved. This means that the filesystem is examined
for each directory in the path, and any symlinks found are followed.

    # bar is a symlink pointing to "/baz"
    my $io = "foo/./bar/..".IO.resolve;      # now "/" (the parent of "/baz")

If C<:$completely>, which defaults to C<False>, is set to a true value, the
method will L«C<fail>|/routine/fail» with C<X::IO::Resolve> if it cannot
completely resolve the path, otherwise, it will resolve as much as possible, and
will merely perform L«C<cleanup>|/routine/cleanup» of the rest of the path. The
last part of the path does B<NOT> have to exist to C<:$completely> resolve the
path.

NOTE: Currently (April 2017) this method doesn't work correctly on all
platforms, e.g. Windows, since C<resolve> assumes POSIX semantics.

=head2 routine dir

Defined as:

    multi sub dir(*%_)
    multi sub dir(IO::Path:D $path, |c)
    multi sub dir(IO()       $path, |c)
    method dir(IO::Path:D: Mu :$test = $*SPEC.curupdir)

Returns the contents of a directory as a lazy list of C<IO::Path> objects
representing relative paths, filtered by
L<smartmatching|/language/operators#infix_~~> their names (as strings) against
the C<:test> parameter. The path of returned files will be absolute or
relative depending on what C<$path> is.

Since the tests are performed against C<Str> arguments, not C<IO>, the tests are
executed in the C<$*CWD>, instead of the target directory. When testing against
file test operators, this won't work:

    dir('mydir', test => { .IO.d })

while this will:

    dir('mydir', test => { "mydir/$_".IO.d })

B<NOTE:> a C<dir> call opens a directory for reading, which counts towards
maximum per-process open files for your program. Be sure to exhaust returned
L<Seq|/type/Seq> before doing something like recursively performing more C<dir>
calls. You can exhaust it by assigning to a C<@->sigiled variable or simply
looping over it. Note how examples below push further dirs to look through into
an L<Array|/type/Array>, rather than immediately calling C<dir> on them. See
also L«C<IO::Dir> module|https://modules.raku.org/dist/IO::Dir» that gives you
finer control over closing dir handles.

Examples:

    # To iterate over the contents of the current directory:
    for dir() -> $file {
        say $file;
    }

    # As before, but include even '.' and '..' which are filtered out by
    # the default :test matcher:
    for dir(test => *) -> $file {
        say $file;
    }

    # To get the names of all .jpg and .jpeg files in the home directory of the current user:
    my @jpegs = $*HOME.dir: test => /:i '.' jpe?g $/;

An example program that lists all files and directories recursively:

    sub MAIN($dir = '.') {
        my @todo = $dir.IO;
        while @todo {
            for @todo.pop.dir -> $path {
                say $path.Str;
                @todo.push: $path if $path.d;
            }
        }
    }

A lazy way to find the first three files ending in ".p6" recursively
starting from the current directory:

=for code
my @stack = '.'.IO;
my $raku-files = gather while @stack {
    with @stack.pop {
        when :d { @stack.append: .dir }
        .take when .extension.lc eq 'p6'
    }
}
.put for $raku-files[^3];

=head2 File test operators

For most file tests, you can do a smartmatch C<~~> or you can call a method.
You don't need to actually open a filehandle in the traditional way (although
you can) to do a filetest. You can simply append C<.IO> to the filename and
smartmatch it to a test adverb. For
instance, here is how to check whether a file is readable using smartmatch:

    '/path/to/file'.IO ~~ :r;

File tests include:

=item C<:d> (L«Directory|/type/IO::Path#method_d»)
=item C<:e> (L«Exists|/type/IO::Path#method_e»)
=item C<:f> (L«File|/type/IO::Path#method_f»)
=item C<:l> (L«Symbolic link|/type/IO::Path#method_l»)
=item C<:r> (L«Readable|/type/IO::Path#method_r»)
=item C<:rw> (L«Readable and writable|/type/IO::Path#method_rw»)
=item C<:rwx> (L«Readable, writable and executable|/type/IO::Path#method_rwx»)
=item C<:s> (L«Size|/type/IO::Path#method_s»)
=item C<:w> (L«Writable|/type/IO::Path#method_w»)
=item C<:x> (L«Executable|/type/IO::Path#method_x»)
=item C<:z> (L«Zero size|/type/IO::Path#method_z»)

These tests will not cache the results of earlier test executions.

L«Smartmatching on Pairs|/type/Pair#method_ACCEPTS» can be used to perform
multiple tests at once:

    say :d & :x;                # OUTPUT: «all(d => True, x => True)␤»
    say '/tmp'.IO ~~ :d & :x;   # OUTPUT: «True␤»
    say '/'.IO    ~~ :d & :rw;  # OUTPUT: «False␤»

All of the above tests can be used as methods (without the colon), though method
tests may throw C<X::IO::DoesNotExist> as documented below.  Three tests only
exist as methods: L«C<accessed>|#method_accessed», L«C<changed>|#method_changed»
and L«C<modified>|#method_modified».

You can also perform file tests on an already opened filehandle by testing
against its L«C<.path>|/type/IO::Handle#method_path» method. For example, given
filehandle C<$fh>:

=for code :preamble<my $fh>
$fh.path ~~ :r;
$fh.path.r;       # method form

=head2 method e

Defined as:

    method e(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists.

=head2 method d

Defined as:

    method d(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is a directory.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

=head2 method f

Defined as:

    method f(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is a file. The method
will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the path points to
a non-existent filesystem entity.

=head2 method s

Defined as:

    method s(IO::Path:D: --> Int:D)

Returns the file size in bytes. May be called on paths that are directories, in
which case the reported size is dependent on the operating system. The method
will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the path points to
a non-existent filesystem entity.

    say $*EXECUTABLE.IO.s; # OUTPUT: «467␤»

=head2 method l

Defined as:

    method l(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is a symlink.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

=head2 method r

Defined as:

    method r(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is accessible.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

=head2 method w

Defined as:

    method w(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is writable.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

=head2 method rw

Defined as:

    method rw(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is readable and
writable. The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist>
if the path points to a non-existent filesystem entity.

=head2 method x

Defined as:

    method x(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is executable.
The method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the
path points to a non-existent filesystem entity.

B<NOTE:> If the file is a script (an executable text file and not a native executable),
and the file has I<only> executable permissions and I<no> read permissions,
this method will return C<True> but trying to execute will fail. That is a
limitation of the operating system.

=head2 method rwx

Defined as:

    method rwx(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and is executable,
readable, and writable. The method will L«C<fail>|/routine/fail» with
C<X::IO::DoesNotExist> if the path points to a non-existent filesystem entity.

=head2 method z

Defined as:

    method z(IO::Path:D: --> Bool:D)

Returns C<True> if the invocant is a path that exists and has size of C<0>. May
be called on paths that are directories, in which case the reported file size
(and thus the result of this method) is dependent on the operating system. The
method will L«C<fail>|/routine/fail» with C<X::IO::DoesNotExist> if the path
points to a non-existent filesystem entity.

=head2 method sibling

Defined as:

    method sibling(IO::Path:D: Str() $sibling --> IO::Path:D)

Allows to reference a sibling file or directory. Returns a new
L«C<IO::Path>|/type/IO::Path» based on the invocant, with the
L«C<.basename>|/type/IO::Path#method_basename» changed to C<$sibling>. The
C<$sibling> is allowed to be a multi-part path fragment; see also
L«C<.add>|/type/IO::Path#method_add».

    say '.bashrc'.IO.sibling: '.bash_aliases'; # OUTPUT: «.bash_aliases".IO␤»
    say '/home/camelia/.bashrc'.IO.sibling: '.bash_aliases';
    # OUTPUT: «/home/camelia/.bash_aliases".IO␤»

    say '/foo/' .IO.sibling: 'bar';  # OUTPUT: «/bar".IO␤»
    say '/foo/.'.IO.sibling: 'bar';  # OUTPUT: «/foo/bar".IO␤»

=head2 method words

Defined as:

    method words(IO::Path:D: :$chomp = True, :$enc = 'utf8', :$nl-in = ["\x0A", "\r\n"], |c --> Seq:D)

Opens the invocant and returns its L«words|/type/IO::Handle#routine_words».

The behavior is equivalent to L<opening|/routine/open> the file specified by
the invocant, forwarding the C<:$chomp>, C<:$enc>, and C<:$nl-in> arguments to
L«C<IO::Handle.open>|/type/IO::Handle#method_open», then calling
L«C<IO::Handle.words>|/type/IO::Handle#routine_words» on that handle, forwarding
any of the remaining arguments to that method, and returning the resultant
L<Seq|/type/Seq>.

B<NOTE:> words are lazily read. The handle used under the hood is not closed
until the returned L<Seq|/type/Seq> is
L<fully reified|/language/glossary#index-entry-Reify>, and this could lead to
leaking open filehandles. It is possible to avoid leaking open filehandles using
the L«C<$limit> argument|/type/IO::Handle#routine_words» to cut down the C<Seq>
of words to be generated.

=for code
my %dict := bag 'my-file.txt'.IO.words;
say "Most common words: ", %dict.sort(-*.value).head: 5;

=head2 method lines

Defined as:

    method lines(IO::Path:D: :$chomp = True, :$enc = 'utf8', :$nl-in = ["\x0A", "\r\n"], |c --> Seq:D)

Opens the invocant and returns its L«lines|/type/IO::Handle#routine_lines».

The behavior is equivalent to L<opening|/routine/open> the file specified by
the invocant, forwarding the C<:$chomp>, C<:$enc>, and C<:$nl-in> arguments to
L«C<IO::Handle.open>|/type/IO::Handle#method_open», then calling
L«C<IO::Handle.lines>|/type/IO::Handle#routine_lines» on that handle, forwarding
any of the remaining arguments to that method, and returning the resultant
L<Seq|/type/Seq>.

B<NOTE:> the lines are ready lazily and the handle used under the hood won't
get closed until the returned L<Seq|/type/Seq> is
L<fully reified|/language/glossary#index-entry-Reify>, so ensure it is,
or you'll be leaking open filehandles. (TIP: use the
L«C<$limit> argument|/type/IO::Handle#routine_lines»)

=begin code
say "The file contains ",
  '50GB-file'.IO.lines.grep(*.contains: 'Raku').elems,
  " lines that mention Raku";
# OUTPUT: «The file contains 72 lines that mention Raku␤»
=end code

=head2 routine slurp

Defined as:

    multi method slurp(IO::Path:D: :$bin, :$enc)

Read all of the file's content and return it as either L<Buf|/type/Buf>, if
C<:$bin> is C<True>, or if not, as L<Str|/type/Str> decoded with C<:$enc>
encoding, which defaults to C<utf8>. File will be closed afterwards. See
L«C<&open>|/routine/open» for valid values for C<:$enc>.

=head2 method spurt

Defined as:

    method spurt(IO::Path:D: $data, :$enc, :$append, :$createonly)

Opens the path for writing, and writes all of the C<$data> into it. File
will be closed afterwards. Will L«C<fail>|/routine/fail» if it cannot succeed
for any reason. The C<$data> can be any L«C<Cool>|/type/Cool» type or any
L«C<Blob>|/type/Blob» type. Arguments are as follows:

=item C<:$enc> — character encoding of the data. Takes same values as C<:$enc>
in L«C<IO::Handle.open>|/routine/open». Defaults to C<utf8>. Ignored if C<$data>
is a L«C<Blob>|/type/Blob».

=item C<:$append> — open the file in C<append> mode, preserving existing
contents, and appending data to the end of the file.

=item C<:$createonly> — L«C<fail>|/routine/fail» if the file already exists.

=head2 method chdir

Defined as:

    multi method chdir(IO::Path:D: IO $path, |c)
    multi method chdir(IO::Path:D: Str() $path, :$d = True, :$r, :$w, :$x)

Contrary to the name, the C<.chdir> method does not change any directories,
but merely concatenates the given C<$path> to the invocant and returns the
resultant C<IO::Path>. Optional file tests can be performed by providing
C<:d>, C<:r>, C<:w>, or C<:x> L«C<Bool>|/type/Bool» named arguments; when
set to C<True>, they'll perform L«C<.d>|/routine/d», L«C<.r>|/routine/r»,
L«C<.w>|/routine/w», and L«C<.x>|/routine/x» tests respectively. By default,
only C<:d> is set to C<True>.

=head2 method mkdir

Defined as:

    method mkdir(IO::Path:D: Int() $mode = 0o777 --> IO::Path:D)

Creates a new directory, including its parent directories, as needed (similar to *nix utility C<mkdir> with C<-p> option).
That is, C<mkdir "foo/bar/ber/meow"> will create C<foo>, C<foo/bar>, and C<foo/bar/ber>
directories as well if they do not exist.

Returns the L<IO::Path|/type/IO::Path> object pointing to
the newly created directory on success;
L<fails|/routine/fail> with L<X::IO::Mkdir|/type/X::IO::Mkdir> if directory cannot be created.

See also L«C<mode>|/routine/mode» for explanation and
valid values for C<$mode>.

=head2 routine rmdir

Defined as:

    sub    rmdir(*@dirs --> List:D)
    method rmdir(IO::Path:D: --> True)

Remove the invocant, or in sub form, all of the provided directories in the
given list, which can contain any L<Cool|/type/Cool> object. Only works on empty
directories.

Method form returns C<True> on success and returns a L<Failure|/type/Failure>
of type C<X::IO::Rmdir> if the directory cannot be removed (e.g.
the directory is not empty, or the path is not a directory). Subroutine
form returns a list of directories that were successfully deleted.

To delete non-empty directory, see L«rmtree in C<File::Directory::Tree> module|https://github.com/labster/p6-file-directory-tree».

=head2 method chmod

Defined as:

    method chmod(IO::Path:D: Int() $mode --> Bool)

Changes the POSIX permissions of a file or directory to C<$mode>.
Returns C<True> on success; on failure,
L<fails|/routine/fail> with L<X::IO::Chmod|/type/X::IO::Chmod>.

The mode is expected as an integer following the L<standard numeric notation|https://en.wikipedia.org/wiki/File_system_permissions#Numeric_notation>, and is
best written as an octal number:

=for code
'myfile'.IO.chmod(0o444);          # make a file read-only
'somedir'.IO.chmod(0o777);         # set 0777 permissions on a directory

Make sure you I<don't> accidentally pass the intended octal digits as a decimal
number (or string containing a decimal number):

=for code
'myfile'.IO.chmod:  '0444';        # BAD!!! (interpreted as mode 0o674)
'myfile'.IO.chmod: '0o444';        # OK (an octal in a string)
'myfile'.IO.chmod:  0o444;         # Also OK (an octal literal)

=head2 routine rename

Defined as:

    method rename(IO::Path:D: IO() $to, :$createonly = False --> Bool:D)
    sub    rename(IO() $from, IO() $to, :$createonly = False --> Bool:D)

Renames a file or directory. Returns C<True> on success; L<fails|/routine/fail>
with L<X::IO::Rename|/type/X::IO::Rename> if C<:$createonly> is C<True> and the C<$to> path already
exists or if the operation failed for some other reason.

B<Note:> some renames will always fail, such as when the new name is on a
different storage device. See also: L«C<move>|/routine/move».

=head2 routine copy

Defined as:

    method copy(IO::Path:D: IO() $to, :$createonly --> Bool:D)
    sub    copy(IO() $from, IO() $to, :$createonly --> Bool:D)

Copies a file. Returns C<True> on success; L<fails|/routine/fail>
with L<X::IO::Copy|/type/X::IO::Copy> if C<:$createonly> is C<True> and the C<$to> path already
exists or if the operation failed for some other reason, such as when
C<$to> and C<$from> are the same file.

=head2 routine move

Defined as:

    method move(IO::Path:D: IO() $to, :$createonly --> Bool:D)
    sub    move(IO() $from, IO() $to, :$createonly --> Bool:D)

Copies a file and then removes the original. If removal fails, it's possible
to end up with two copies of the file. Returns C<True> on success;
L<fails|/routine/fail> with L<X::IO::Move|/type/X::IO::Move> if C<:$createonly> is C<True> and
the C<$to> path already exists or if the operation failed for some other reason,
such as when C<$to> and C<$from> are the same file.

To avoid copying, you can use L«C<rename>|/routine/rename», if the files are on
the same storage device. It also works with directories, while C<move> does not.

=head2 method Numeric

Defined as:

    method Numeric(IO::Path:D: --> Numeric:D)

Coerces L«C<.basename>|/routine/basename» to L<Numeric|/type/Numeric>. L<Fails|/routine/fail>
with C<X::Str::Numeric> if base name is not numerical.

=head2 method Int

Defined as:

    method Int(IO::Path:D: --> Int:D)

Coerces L«C<.basename>|/routine/basename» to L<Int|/type/Int>. L<Fails|/routine/fail>
with C<X::Str::Numeric> if base name is not numerical.

X<|symlink (sub)>
X<|symlink (method)>
=head2 routine symlink

Defined as:

    method symlink(IO::Path:D $target: IO() $link, Bool :$absolute = True --> Bool:D)
    sub    symlink(      IO() $target, IO() $link, Bool :$absolute = True --> Bool:D)

Create a new I<symbolic> link C<$link> to existing C<$target>.
Returns C<True> on success; L<fails|/routine/fail> with
L<X::IO::Symlink|/type/X::IO::Symlink> if the symbolic link could not be created. If C<$target>
does not exist, creates a dangling symbolic link.

C<symlink> creates a symbolic link using an absolute path
by default. To create a relative symlink set the C<absolute>
parameter to C<False> e.g. C<:!absolute>. This flag was
introduced in Rakudo version 2020.11.

To create a hard link, see L«C<link>|/routine/link».

B<Note:> on Windows, creation of symbolic links may require escalated
privileges.

X<|link (sub)>
X<|link (method)>
=head2 routine link

Defined as:

    method link(IO::Path:D $target: IO() $link --> Bool:D)
    sub    link(      IO() $target, IO() $link --> Bool:D)

Create a new I<hard> link C<$link> to existing C<$target>.
Returns C<True> on success; L<fails|/routine/fail> with
L<X::IO::Link|/type/X::IO::Link> if the hard link could not be created.
To create a symbolic link, see L«C<symlink>|/routine/symlink».

=head2 routine unlink

Defined as:

    method unlink(IO::Path:D: --> True)
    sub    unlink(*@filenames --> List:D)

Delete all specified ordinary files, links, or symbolic links for which there
are privileges to do so. See L<rmdir|/routine/rmdir> to delete directories.

The subroutine form returns the names of all the files in the list, excluding
those for which the filesystem raised some error; since trying to delete a file
that does not exist does not raise any error at that level, this list will
include the names of the files in the list that do not exist.

The method form returns C<True> on success, or L<fails|/routine/fail>
with L<X::IO::Unlink|/type/X::IO::Unlink> if the operation could not be completed. If the file to be
deleted does not exist, the routine treats it as success.

=begin code
'foo.txt'.IO.open(:w).close;
'bar'.IO.mkdir;
say unlink <foo.txt  bar  not-there.txt>; # OUTPUT: «[foo.txt not-there.txt]␤»
# `bar` is not in output because it failed to delete (it's a directory)
# `not-there.txt` is present. It never existed, so that's deemed a success.

# Method form `fail`s:
say .exception.message without 'bar'.IO.unlink;
# OUTPUT: «Failed to remove the file […] illegal operation on a directory␤»
=end code

=head2 method IO

Defined as:

    method IO(IO::Path:D: --> IO::Path)

Returns the invocant.

=head2 method SPEC

Defined as:

    method SPEC(IO::Path:D: --> IO::Spec)

Returns the L<IO::Spec|/type/IO::Spec> object that was (implicitly) specified at
object creation time.

    my $io = IO::Path.new("/bin/bash");
    say $io.SPEC;                            # OUTPUT: «(Unix)␤»
    say $io.SPEC.dir-sep;                    # OUTPUT: «/␤»

=head1 File timestamp retrieval

There are also 3 methods for fetching the 3 timestamps of a file (inode),
on Operating Systems where these are available:

=head2 method modified

Returns an L«C<Instant>|/type/Instant» object indicating when the content
of the file was last modified. Compare with L<changed|/routine/changed>.

=for code
say "path/to/file".IO.modified;          # Instant:1424089165
say "path/to/file".IO.modified.DateTime; # 2015-02-16T12:18:50Z

=head2 method accessed

Return an L<Instant|/type/Instant> object representing the timestamp when the file was
last accessed. B<Note:> depending on how the filesystem was mounted, the
last accessed time may not update on I<each access> to the file, but only
on the first access after modifications.

=for code
say "path/to/file".IO.accessed;          # Instant:1424353577
say "path/to/file".IO.accessed.DateTime; # 2015-02-19T13:45:42Z

=head2 method changed

Returns an L«C<Instant>|/type/Instant» object indicating the metadata of
the file or directory was last changed (e.g. permissions, or files
created/deleted in directory). Compare with L<modified|/routine/modified>.

=for code
say "path/to/file".IO.changed;           # Instant:1424089165
say "path/to/file".IO.changed.DateTime;  # 2015-02-16T12:18:50Z

=head1 File permissions retrieval

=head2 method mode

Return an L<IntStr|/type/IntStr> object representing the POSIX permissions of a file.  The
C<Str> part of the result is the octal representation of the file permission,
like the form accepted by the C<chmod(1)> utility.

=for code
say ~"path/to/file".IO.mode;  # e.g. '0644'
say +"path/to/file".IO.mode;  # e.g. 420, where sprintf('%04o', 420) eq '0644'

The result of this can be used in the other methods that take a mode as an
argument.

=for code
"path/to/file1".IO.chmod("path/to/file2".IO.mode);  # will change the
                                                    # permissions of file1
                                                    # to be the same as file2


=end pod

# vim: expandtab shiftwidth=4 ft=perl6
